package com.broada.apm.protocol;

import java.io.*;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;

import com.google.protobuf.ByteString;
import org.zeromq.ZFrame;
import org.zeromq.ZMQ;
import org.zeromq.ZMsg;

import com.broada.apm.ApmServer;

/**
 * 协议分析器交互接口 Author: jialin
 *
 * 2016.5.30 jialin 修改ssl接口，改用直接发送content给协议分析器，而不是文件
 * 2015.9.24 jialin 新增lua接口
 * 2015.4.10 jialin bugfix: sendcommand() byte array error
 * 2015.4.8 jialin  Command接口返回值调整为CommandResult
 * 2015.3.31 jialin init data
 */
public class ZeroClient implements Closeable {
    public static final String DEFAULT_SERVER_ADDR = ApmServer.APM_PROTOCOL_HOST;

    public static final int TIMEOUT_WAIT_SECONDS = 1; // 等待时间，秒

    public static final int IPPROTO_ANY = 0;
    public static final int IPPROTO_TCP = 6;
    public static final int IPPROTO_UDP = 17;

    public boolean debug_mode = false;

    private int timeout_s = 0; // 0为inifinite等待

    private ZMQ.Context ctxt = ZMQ.context(1);
    private ZMQ.Socket socket = null;

    // change status for commit/rollback
    private boolean module_changed = false;
    private boolean l7_changed = false;
    private boolean l7default_changed = false;
    private boolean filter_changed = false;
    private boolean java_changed = false;
    private boolean lua_changed = false;
    private boolean ssl_changed = false;

    public ZeroClient() {
    	
    }

    // 将10.0.0.0形式的ip地址转换成10进制整数
    public static long ip2Long(String str) {
        String args[] = str.split("\\.");
        if (args.length != 4)
            return 0;

        long[] ip = new long[4];
        for (int i = 0; i < args.length; i++)
            ip[i] = Long.parseLong(args[i]);

        return (ip[0] << 24) + (ip[1] << 16) + (ip[2] << 8) + ip[3];
    }

    // 将10进制整数形式转换成10.0.0.0形式的IP地址
    public static String long2Ip(long longIp) {
        return String.format("%d.%d.%d.%d", longIp >>> 24, (longIp & 0x00FFFFFF) >>> 16, (longIp & 0x0000FFFF) >>> 8,
                longIp & 0x000000FF);
    }

    // server_addr = tcp://192.168.18.104:1108，默认超时1秒
    public void connect(String server_addr) throws Exception {
        connect(server_addr, TIMEOUT_WAIT_SECONDS);
    }

    public void connect(String server_addr, int timeout_s) throws Exception {
        this.timeout_s = timeout_s;
        socket = ctxt.socket(ZMQ.REQ);
        socket.connect(server_addr);
        socket.setSendTimeOut(this.timeout_s * 1000);
        socket.setReceiveTimeOut(this.timeout_s * 1000);
        socket.setLinger(0);
    }

    // 发起请求，返回一个结果
    private int send_request(byte[] msg, OutputStream output) throws Exception {
        ZMsg message_to_send = new ZMsg();
        message_to_send.add(msg);
        message_to_send.send(socket, true);

        ZMsg message_received = ZMsg.recvMsg(socket);
        if (message_received == null)
            return 0;

        for (ZFrame f : message_received) {
            if (f.hasData())
                output.write(f.getData());
        }

        return 1;
    }

    // 发起执行命令，返回一个结果
    // 1 表示正常
    // <=0 表示异常
    private int send_command(byte[] msg, OutputStream output) throws Exception {
        ZMsg message_to_send = new ZMsg();
        message_to_send.add(msg);
        message_to_send.send(socket, true);

        ZMsg message_received = ZMsg.recvMsg(socket);
        if (message_received == null)
            return 0;

        byte[] buf = null;
        for (ZFrame f : message_received) {
            if (f.hasData()) {
                if (buf == null) {
                    // buf = new byte[f.getData().length];
                    // System.arraycopy(f.getData(), 0, buf, 0, f.getData().length);
                    buf = f.getData();
                } else {
                    byte[] newByte = new byte[buf.length + f.getData().length];
                    System.arraycopy(buf, 0, newByte, 0, buf.length);
                    System.arraycopy(f.getData(), 0, newByte, buf.length, f.getData().length);
                    buf = newByte;
                }
            }
        }

        Client.CommandResult result_builder = Client.CommandResult.parseFrom(buf);
        if (result_builder.getStatusCode() == Client.CommandResult.ResultType.Type_OK)
            return 1;
        else {
            if (result_builder.hasReason()) {
                output.write(result_builder.getReason().getBytes());
                output.write("\r\n".getBytes());
            }
            if (result_builder.hasMessage())
                output.write(result_builder.getMessage().getBytes());

            throw new Exception(output.toString());
        }
    }

    // 关闭连接
    public void close() {
        if (socket != null)
            try {
                socket.close();
            } catch (Exception e) {
            }
        if (ctxt != null)
            try {
                ctxt.close();
            } catch (Exception e) {
            }
        ctxt = null;
        socket = null;
    }

    /* Print 信息 */
    private String print_command(String cmd, String args[]) throws Exception {
        Client.Command.Builder cmd_builder = Client.Command.newBuilder();
        cmd_builder.setCmdType(Client.Command.CommandType.Type_PrintCommand);

        Client.PrintCommand.Builder print_builder = Client.PrintCommand.newBuilder();
        print_builder.setCmd(cmd);
        for (int i = 0; args != null && i < args.length; i++)
            print_builder.addArgs(args[i]);
        cmd_builder.setPrintCmd(print_builder);

        Client.Command command = cmd_builder.build();

        // send request
        if (debug_mode)
            System.out.format("Sending request:\n%s\n", command.toString());
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        if (0 == send_request(command.toByteArray(), baos)) {
            System.out.format("Timeout read in %ss\n", timeout_s);
            return null;
        }

        if (debug_mode)
            System.out.format("Received message:\n%s\n", baos.toString());

        return baos.toString();
    }

    // 打印系统总体运行情况
    public String print_system() throws Exception {
        return print_command("system", null);
    }

    // 打印ct信息：print ct
    // l7_proto = all 表示打印所有ct
    // l7_proto = unknown 表示打印未识别的ct
    public String print_ct(String l7_proto) throws Exception {
        return print_command("ct", new String[]{l7_proto});
    }

    // 打印应用拓扑
    public String print_topology() throws Exception {
        return print_command("topology", null);
    }

    // 打印线程信息
    public String print_task() throws Exception {
        return print_command("task", null);
    }

    // 打印模块统计信息
    public String print_module() throws Exception {
        return print_command("module", new String[]{"stat"});
    }

    // 打印服务节点信息
    public String print_servernode() throws Exception {
        return print_command("server", null);
    }

    // 打印数据包过滤规则
    public String print_filter() throws Exception {
        return print_command("filter", null);
    }

    //打印协议分析器版本
    public String print_version() throws Exception {
        return print_command("version", null);
    }

    // 打印内存信息
    public String print_memory() throws Exception {
        return print_command("memory", null);
    }

    // 打印模块配置信息
    public String print_moduleconfig() throws Exception {
        return print_command("module", new String[]{"config"});
    }

    // 打印l7配置信息
    public String print_l7config() throws Exception {
        return print_command("l7", null);
    }

    // 提交信息
    // 注意：clear/add/del等操作只有在commit后才生效
    public void commit() throws Exception {
        if (module_changed)
            commit_module();
        if (l7_changed)
            commit_l7();
        if (filter_changed)
            commit_filter();
        if (l7default_changed)
            commit_l7default();
        if (java_changed)
            commit_java();
        if (lua_changed)
            commit_lua();
        if (ssl_changed)
            commit_ssl();

        module_changed = false;
        l7_changed = false;
        l7default_changed = false;
        filter_changed = false;
        java_changed = false;
        lua_changed = false;
        ssl_changed = false;
    }

    public void rollback() throws Exception {
        if (module_changed)
            rollback_module();
        if (l7_changed)
            rollback_l7();
        if (filter_changed)
            rollback_filter();
        if (l7default_changed)
            rollback_l7default();
        if (java_changed)
            rollback_java();
        if (lua_changed)
            rollback_lua();
        if (ssl_changed)
            rollback_ssl();

        module_changed = false;
        l7_changed = false;
        l7default_changed = false;
        filter_changed = false;
        java_changed = false;
        lua_changed = false;
        ssl_changed = false;
    }

    private boolean commit(Client.CommitCommand.Type cmd_type) throws Exception {
        Client.Command.Builder cmd_builder = Client.Command.newBuilder();
        cmd_builder.setCmdType(Client.Command.CommandType.Type_CommitCommand);

        Client.CommitCommand.Builder commit_builder = Client.CommitCommand.newBuilder();
        commit_builder.setAction(Client.CommitCommand.Action.Commit);
        commit_builder.setCmd(cmd_type);
        cmd_builder.setCommitCmd(commit_builder);

        Client.Command command = cmd_builder.build();

        // send request
        if (debug_mode)
            System.out.format("Sending request:\n%s\n", command.toString());
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        int ret = send_command(command.toByteArray(), baos);
        if (ret <= 0)
            throw new Exception("协议分析器连接超时或调用失败");

        return true;
    }

    private boolean rollback(Client.CommitCommand.Type cmd_type) throws Exception {
        Client.Command.Builder cmd_builder = Client.Command.newBuilder();
        cmd_builder.setCmdType(Client.Command.CommandType.Type_CommitCommand);

        Client.CommitCommand.Builder commit_builder = Client.CommitCommand.newBuilder();
        commit_builder.setAction(Client.CommitCommand.Action.Rollback);
        commit_builder.setCmd(cmd_type);
        cmd_builder.setCommitCmd(commit_builder);

        Client.Command command = cmd_builder.build();

        // send request
        if (debug_mode)
            System.out.format("Sending request:\n%s\n", command.toString());
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        int ret = send_command(command.toByteArray(), baos);
        if (ret <= 0)
            throw new Exception("协议分析器连接超时或调用失败");

        return true;
    }

    // filter
    private boolean commit_filter() throws Exception {
        return commit(Client.CommitCommand.Type.FILTER);
    }

    private boolean rollback_filter() throws Exception {
        return rollback(Client.CommitCommand.Type.FILTER);
    }

    // l7
    private boolean commit_l7() throws Exception {
        return commit(Client.CommitCommand.Type.L7);
    }

    private boolean rollback_l7() throws Exception {
        return rollback(Client.CommitCommand.Type.L7);
    }

    // module
    private boolean commit_module() throws Exception {
        return commit(Client.CommitCommand.Type.MODULE);
    }

    private boolean rollback_module() throws Exception {
        return rollback(Client.CommitCommand.Type.MODULE);
    }

    // l7default
    private boolean commit_l7default() throws Exception {
        return commit(Client.CommitCommand.Type.L7_DEFAULT);
    }

    private boolean rollback_l7default() throws Exception {
        return rollback(Client.CommitCommand.Type.L7_DEFAULT);
    }

    // java
    private boolean commit_java() throws Exception {
        return commit(Client.CommitCommand.Type.JAVA);
    }

    private boolean rollback_java() throws Exception {
        return rollback(Client.CommitCommand.Type.JAVA);
    }

    //lua
    private boolean commit_lua() throws Exception {
        return commit(Client.CommitCommand.Type.LUA);
    }

    private boolean rollback_lua() throws Exception {
        return rollback(Client.CommitCommand.Type.LUA);
    }

    //ssl
    private boolean commit_ssl() throws Exception {
        return commit(Client.CommitCommand.Type.SSL);
    }

    private boolean rollback_ssl() throws Exception {
        return rollback(Client.CommitCommand.Type.SSL);
    }

    // 模块操作
    public boolean module_clear(String path_to_module_xml) throws Exception {
        Client.Command.Builder cmd_builder = Client.Command.newBuilder();
        cmd_builder.setCmdType(Client.Command.CommandType.Type_ModuleCommand);

        Client.ModuleCommand.Builder module_builder = Client.ModuleCommand.newBuilder();
        module_builder.setAction("clear");
        cmd_builder.setModuleCmd(module_builder);

        Client.Command command = cmd_builder.build();

        // send request
        if (debug_mode)
            System.out.format("Sending request:\n%s\n", command.toString());
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        int ret = send_command(command.toByteArray(), baos);
        if (ret <= 0)
            throw new Exception("协议分析器连接超时或调用失败");

        module_changed = true;
        return true;
    }

    // modules/1/moduel.xml
    public boolean module_add(String path_to_module_xml) throws Exception {
        Client.Command.Builder cmd_builder = Client.Command.newBuilder();
        cmd_builder.setCmdType(Client.Command.CommandType.Type_ModuleCommand);

        Client.ModuleCommand.Builder module_builder = Client.ModuleCommand.newBuilder();
        module_builder.setAction("add");
        module_builder.setSoPath(path_to_module_xml);
        cmd_builder.setModuleCmd(module_builder);

        Client.Command command = cmd_builder.build();

        // send request
        if (debug_mode)
            System.out.format("Sending request:\n%s\n", command.toString());
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        int ret = send_command(command.toByteArray(), baos);
        if (ret <= 0)
            throw new Exception("协议分析器连接超时或调用失败");

        module_changed = true;
        return true;
    }

    public boolean module_del(String path_to_module_xml) throws Exception {
        Client.Command.Builder cmd_builder = Client.Command.newBuilder();
        cmd_builder.setCmdType(Client.Command.CommandType.Type_ModuleCommand);

        Client.ModuleCommand.Builder module_builder = Client.ModuleCommand.newBuilder();
        module_builder.setAction("del");
        module_builder.setSoPath(path_to_module_xml);
        cmd_builder.setModuleCmd(module_builder);

        Client.Command command = cmd_builder.build();

        // send request
        if (debug_mode)
            System.out.format("Sending request:\n%s\n", command.toString());
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        int ret = send_command(command.toByteArray(), baos);
        if (ret <= 0)
            throw new Exception("协议分析器连接超时或调用失败");

        module_changed = true;
        return true;
    }

    public boolean module_del(int l7_proto) throws Exception {
        Client.Command.Builder cmd_builder = Client.Command.newBuilder();
        cmd_builder.setCmdType(Client.Command.CommandType.Type_ModuleCommand);

        Client.ModuleCommand.Builder module_builder = Client.ModuleCommand.newBuilder();
        module_builder.setAction("del");
        module_builder.setL7Proto(l7_proto);
        cmd_builder.setModuleCmd(module_builder);

        Client.Command command = cmd_builder.build();

        // send request
        if (debug_mode)
            System.out.format("Sending request:\n%s\n", command.toString());
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        int ret = send_command(command.toByteArray(), baos);
        if (ret <= 0)
            throw new Exception("协议分析器连接超时或调用失败");

        module_changed = true;
        return true;
    }
    
    //删除远程
    public boolean module_delremote(int moduleId) throws Exception {
        Client.Command.Builder cmd_builder = Client.Command.newBuilder();
        cmd_builder.setCmdType(Client.Command.CommandType.Type_ModuleCommand);

        Client.ModuleCommand.Builder module_builder = Client.ModuleCommand.newBuilder();
        module_builder.setAction("remove");
        module_builder.setL7Proto(moduleId);
        cmd_builder.setModuleCmd(module_builder);
        Client.Command command = cmd_builder.build();

        // send request
        if (debug_mode)
            System.out.format("Sending request:\n%s\n", command.toString());
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        int ret = send_command(command.toByteArray(), baos);
        if (ret <= 0)
            throw new Exception("协议分析器连接超时或调用失败");

        module_changed = true;
        return true;
    }
    
    //禁用远程
    public boolean module_disenableremote(int l7_proto) throws Exception {
        Client.Command.Builder cmd_builder = Client.Command.newBuilder();
        cmd_builder.setCmdType(Client.Command.CommandType.Type_ModuleCommand);

        Client.ModuleCommand.Builder module_builder = Client.ModuleCommand.newBuilder();
        module_builder.setAction("del");
        module_builder.setL7Proto(l7_proto);
        cmd_builder.setModuleCmd(module_builder);

        Client.Command command = cmd_builder.build();

        // send request
        if (debug_mode)
            System.out.format("Sending request:\n%s\n", command.toString());
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        int ret = send_command(command.toByteArray(), baos);
        if (ret <= 0)
            throw new Exception("协议分析器连接超时或调用失败");

        module_changed = true;
        return true;
    }

    //启用远程
    public boolean module_enableremote(int l7_proto) throws Exception {
        Client.Command.Builder cmd_builder = Client.Command.newBuilder();
        cmd_builder.setCmdType(Client.Command.CommandType.Type_ModuleCommand);

        Client.ModuleCommand.Builder module_builder = Client.ModuleCommand.newBuilder();
        module_builder.setAction("add");
        module_builder.setL7Proto(l7_proto);
        cmd_builder.setModuleCmd(module_builder);

        Client.Command command = cmd_builder.build();

        // send request
        if (debug_mode)
            System.out.format("Sending request:\n%s\n", command.toString());
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        int ret = send_command(command.toByteArray(), baos);
        if (ret <= 0)
            throw new Exception("协议分析器连接超时或调用失败");

        module_changed = true;
        return true;
    }
    
    //添加远程
    public boolean module_uploadremote(int l7_proto, String path_to_module_xml) throws Exception {
        File f_module_xml = new File(path_to_module_xml);
        if (!f_module_xml.exists() || !f_module_xml.isFile())
            throw new Exception("File not exists OR invalid: " + path_to_module_xml);

        //开始加载文件
        byte[] mod_bin = null;
        FileChannel fc = null;
        try {
            fc = new RandomAccessFile(f_module_xml, "r").getChannel();
            MappedByteBuffer byteBuffer = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()).load();
            mod_bin = new byte[(int) fc.size()];
            if (byteBuffer.remaining() > 0)
                byteBuffer.get(mod_bin, 0, byteBuffer.remaining());
        } finally {
            try {
                fc.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        //与协议分析器交互，使用二进制流提交mod文件
        Client.Command.Builder cmd_builder = Client.Command.newBuilder();
        cmd_builder.setCmdType(Client.Command.CommandType.Type_ModuleCommand);

        Client.ModuleCommand.Builder module_builder = Client.ModuleCommand.newBuilder();
        module_builder.setAction("upload");
        module_builder.setL7Proto(l7_proto);
        module_builder.setModBin(ByteString.copyFrom(mod_bin));
        cmd_builder.setModuleCmd(module_builder);

        Client.Command command = cmd_builder.build();

        // send request
        if (debug_mode)
            System.out.format("Sending request:\n%s\n", command.toString());
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        int ret = send_command(command.toByteArray(), baos);
        if (ret <= 0)
            throw new Exception("协议分析器连接超时或调用失败");

        module_changed = true;
        return true;
    }

    // L7设置
    public boolean l7_clear() throws Exception {
        Client.Command.Builder cmd_builder = Client.Command.newBuilder();
        cmd_builder.setCmdType(Client.Command.CommandType.Type_L7Command);

        Client.L7Command.Builder l7_builder = Client.L7Command.newBuilder();
        l7_builder.setAction("clear");
        cmd_builder.setL7Cmd(l7_builder);

        Client.Command command = cmd_builder.build();

        // send request
        if (debug_mode)
            System.out.format("Sending request:\n%s\n", command.toString());
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        int ret = send_command(command.toByteArray(), baos);
        if (ret <= 0)
            throw new Exception("协议分析器连接超时或调用失败");

        l7_changed = true;
        return true;
    }

    public boolean l7_set(int ip_proto, long ipaddr_num, int port, int l7_proto) throws Exception {
        return l7_set(ip_proto, ipaddr_num, String.valueOf(port), l7_proto);
    }

    // ip_proto=0, 表示any
    // ip_proto=6, 表示tcp
    // ip_proto=17, 表示udp
    public boolean l7_set(int ip_proto, long ipaddr_num, String port_range, int l7_proto) throws Exception {
        Client.Command.Builder cmd_builder = Client.Command.newBuilder();
        cmd_builder.setCmdType(Client.Command.CommandType.Type_L7Command);

        Client.Command.Builder del_cmd_builder = Client.Command.newBuilder();
        del_cmd_builder.setCmdType(Client.Command.CommandType.Type_L7Command);

        Client.L7Setting.Builder setting_builder = Client.L7Setting.newBuilder();
        setting_builder.setIpProto(ip_proto);
        setting_builder.setIp((int) ipaddr_num);
        setting_builder.setPort(port_range);
        setting_builder.setL7Proto(l7_proto);
        //先删除老的
        Client.L7Command.Builder dell7_builder = Client.L7Command.newBuilder();
        dell7_builder.setAction("delete");
        dell7_builder.addL7Settings(setting_builder);
        del_cmd_builder.setL7Cmd(dell7_builder);
        //添加新的
        Client.L7Command.Builder l7_builder = Client.L7Command.newBuilder();
        l7_builder.setAction("add");
        l7_builder.addL7Settings(setting_builder);
        cmd_builder.setL7Cmd(l7_builder);

        Client.Command command = cmd_builder.build();
        Client.Command delcommand = del_cmd_builder.build();
        // send request
        if (debug_mode)
            System.out.format("Sending request:\n%s\n", command.toString());
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        int del_ret = send_command(delcommand.toByteArray(), baos);
        if (del_ret <= 0)
            throw new Exception("协议分析器连接超时或调用失败");

        int ret = send_command(command.toByteArray(), baos);
        if (ret <= 0)
            throw new Exception("协议分析器连接超时或调用失败");

        l7_changed = true;
        return true;
    }

    public boolean l7_unset(int ip_proto, long ipaddr_num, int port) throws Exception {
        return l7_unset(ip_proto, ipaddr_num, String.valueOf(port));
    }

    public boolean l7_unset(int ip_proto, long ipaddr_num, String port_range) throws Exception {
        Client.Command.Builder cmd_builder = Client.Command.newBuilder();
        cmd_builder.setCmdType(Client.Command.CommandType.Type_L7Command);

        Client.L7Setting.Builder setting_builder = Client.L7Setting.newBuilder();
        setting_builder.setIpProto(ip_proto);
        setting_builder.setIp((int) ipaddr_num);
        setting_builder.setPort(port_range);

        Client.L7Command.Builder l7_builder = Client.L7Command.newBuilder();
        l7_builder.setAction("del");
        l7_builder.addL7Settings(setting_builder);
        cmd_builder.setL7Cmd(l7_builder);

        Client.Command command = cmd_builder.build();

        // send request
        if (debug_mode)
            System.out.format("Sending request:\n%s\n", command.toString());
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        int ret = send_command(command.toByteArray(), baos);
        if (ret <= 0)
            throw new Exception("协议分析器连接超时或调用失败");

        l7_changed = false;
        return true;
    }

    // L7缺省协议设置
    public boolean l7default_clear() throws Exception {
        Client.Command.Builder cmd_builder = Client.Command.newBuilder();
        cmd_builder.setCmdType(Client.Command.CommandType.Type_L7Default);

        Client.L7Default.Builder l7_builder = Client.L7Default.newBuilder();
        l7_builder.setAction("clear");
        cmd_builder.setL7Default(l7_builder);

        Client.Command command = cmd_builder.build();

        // send request
        if (debug_mode)
            System.out.format("Sending request:\n%s\n", command.toString());
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        int ret = send_command(command.toByteArray(), baos);
        if (ret <= 0)
            throw new Exception("协议分析器连接超时或调用失败");

        l7default_changed = true;
        return true;
    }

    public boolean l7default_add(int ip_proto, int l7_proto) throws Exception {
        Client.Command.Builder cmd_builder = Client.Command.newBuilder();
        cmd_builder.setCmdType(Client.Command.CommandType.Type_L7Default);

        Client.L7Setting.Builder setting_builder = Client.L7Setting.newBuilder();
        setting_builder.setIpProto(ip_proto);
        setting_builder.setL7Proto(l7_proto);

        Client.L7Command.Builder l7_builder = Client.L7Command.newBuilder();
        l7_builder.setAction("add");
        l7_builder.addL7Settings(setting_builder);
        cmd_builder.setL7Cmd(l7_builder);

        Client.Command command = cmd_builder.build();

        // send request
        if (debug_mode)
            System.out.format("Sending request:\n%s\n", command.toString());
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        int ret = send_command(command.toByteArray(), baos);
        if (ret <= 0)
            throw new Exception("协议分析器连接超时或调用失败");

        l7default_changed = true;
        return true;
    }

    public boolean l7default_del(int ip_proto) throws Exception {
        Client.Command.Builder cmd_builder = Client.Command.newBuilder();
        cmd_builder.setCmdType(Client.Command.CommandType.Type_L7Default);

        Client.L7Setting.Builder setting_builder = Client.L7Setting.newBuilder();
        setting_builder.setIpProto(ip_proto);

        Client.L7Command.Builder l7_builder = Client.L7Command.newBuilder();
        l7_builder.setAction("del");
        l7_builder.addL7Settings(setting_builder);
        cmd_builder.setL7Cmd(l7_builder);

        Client.Command command = cmd_builder.build();

        // send request
        if (debug_mode)
            System.out.format("Sending request:\n%s\n", command.toString());
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        int ret = send_command(command.toByteArray(), baos);
        if (ret <= 0)
            throw new Exception("协议分析器连接超时或调用失败");

        l7default_changed = true;
        return true;
    }

    // 设置数据包过滤规则
    public boolean filter_clear() throws Exception {
        Client.Command.Builder cmd_builder = Client.Command.newBuilder();
        cmd_builder.setCmdType(Client.Command.CommandType.Type_FilterCommand);

        Client.FilterCommand.Builder filter_builder = Client.FilterCommand.newBuilder();
        filter_builder.setAction("clear");
        cmd_builder.setFilterCmd(filter_builder);

        Client.Command command = cmd_builder.build();

        // send request
        if (debug_mode)
            System.out.format("Sending request:\n%s\n", command.toString());
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        int ret = send_command(command.toByteArray(), baos);
        if (ret <= 0)
            throw new Exception("协议分析器连接超时或调用失败");

        filter_changed = true;
        return true;
    }

    // 设置缺省协议
    public boolean filter_setdefault(boolean accept) throws Exception {
        return filter_add(0, "0.0.0.0", "0", "0.0.0.0", "0", accept);
    }

    // ip_proto=0, 表示any
    // ip_from=IP地址段, 192.168.0.1-192.168.0.100
    // port_from=端口范围, 80-90
    public boolean filter_add(int ip_proto, String ip_range, String port_range, String dstIp_range, String DstPort_range, boolean accept) throws Exception {
        Client.Command.Builder cmd_builder = Client.Command.newBuilder();
        cmd_builder.setCmdType(Client.Command.CommandType.Type_FilterCommand);

        Client.FilterCommand.Builder filtercmd_builder = Client.FilterCommand.newBuilder();
        filtercmd_builder.setAction("add");

        Client.Filter.Builder filter_builder = Client.Filter.newBuilder();
        filter_builder.setIpProto(ip_proto);
        if (ip_range != null)
            filter_builder.setSip(ip_range);
        if (dstIp_range != null)
            filter_builder.setIp(dstIp_range);
        if (port_range != null)
            filter_builder.setSport(port_range);
        if (DstPort_range != null)
            filter_builder.setPort(DstPort_range);

        filter_builder.setAccept(accept ? 1 : 0);
        filtercmd_builder.addFilters(filter_builder);

        cmd_builder.setFilterCmd(filtercmd_builder);

        Client.Command command = cmd_builder.build();

        // send request
        if (debug_mode)
            System.out.format("Sending request:\n%s\n", command.toString());
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        int ret = send_command(command.toByteArray(), baos);
        if (ret <= 0)
            throw new Exception("协议分析器连接超时或调用失败");

        filter_changed = true;
        return true;
    }

    // 不支持删除，请使用filter_clear 和 filter_add 组合
    public boolean filter_del(int ip_proto, String ip_range, String port_range, boolean accept)
            throws Exception {
        if (debug_mode)
            System.out.println("Not implemented yet, please use filter_clear and filter_add instead.");
        filter_changed = false;
        return false;
    }

    // 设置java接口
    public boolean java_setup(int port) throws Exception {
        Client.Command.Builder cmd_builder = Client.Command.newBuilder();
        cmd_builder.setCmdType(Client.Command.CommandType.Type_JavaCommand);

        Client.JavaCommand.Builder java_builder = Client.JavaCommand.newBuilder();
        java_builder.setFamily("tcp");
        java_builder.setPort(port);

        cmd_builder.setJavaCmd(java_builder);

        Client.Command command = cmd_builder.build();

        // send request
        if (debug_mode)
            System.out.format("Sending request:\n%s\n", command.toString());
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        int ret = send_command(command.toByteArray(), baos);
        if (ret <= 0)
            throw new Exception("协议分析器连接超时或调用失败");

        java_changed = true;
        return true;
    }

    // lua接口
    // ipaddr_num: 目标主机IP
    // port: 目标主机端口号
    // lua_script: lua代码
    public boolean lua_setup(long ipaddr_num, int port, String lua_script) throws Exception {
        if (ipaddr_num == 0 || port == 0)
            throw new Exception("请输入目标主机和端口号");

        //删除
        if (lua_script == null || lua_script.isEmpty()) {
            return lua_del(ipaddr_num, port);
        } else { //添加
            return lua_add(ipaddr_num, port, lua_script);
        }
    }

    private boolean lua_add(long ipaddr_num, int port, String lua_script) throws Exception {
        Client.Command.Builder cmd_builder = Client.Command.newBuilder();
        cmd_builder.setCmdType(Client.Command.CommandType.Type_LuaCommand);

        Client.LuaCommand.Builder lua_builder = Client.LuaCommand.newBuilder();
        lua_builder.setAction("add");
        lua_builder.setIp((int) ipaddr_num);
        lua_builder.setPort(String.valueOf(port));
        try {
            lua_builder.setLua(new String(lua_script.toString().getBytes("UTF-8")));
        } catch (Exception e) {
        }
        cmd_builder.setLuaCmd(lua_builder);

        Client.Command command = cmd_builder.build();

        // send request
        if (debug_mode)
            System.out.format("Sending request:\n%s\n", command.toString());
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        int ret = send_command(command.toByteArray(), baos);
        if (ret <= 0)
            throw new Exception("协议分析器连接超时或调用失败");

        lua_changed = true;
        commit_lua();
        return true;
    }

    private boolean lua_del(long ipaddr_num, int port) throws Exception {
        Client.Command.Builder cmd_builder = Client.Command.newBuilder();
        cmd_builder.setCmdType(Client.Command.CommandType.Type_LuaCommand);

        Client.LuaCommand.Builder lua_builder = Client.LuaCommand.newBuilder();
        lua_builder.setAction("del");
        lua_builder.setIp((int) ipaddr_num);
        lua_builder.setPort(String.valueOf(port));
        cmd_builder.setLuaCmd(lua_builder);

        Client.Command command = cmd_builder.build();

        // send request
        if (debug_mode)
            System.out.format("Sending request:\n%s\n", command.toString());
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        int ret = send_command(command.toByteArray(), baos);
        if (ret <= 0)
            throw new Exception("协议分析器连接超时或调用失败");

        lua_changed = true;
        commit_lua();
        return true;
    }
    
    /**
     * 清空lua
     * @return
     * @throws Exception
     */
    public boolean lua_clear() throws Exception {
        Client.Command.Builder cmd_builder = Client.Command.newBuilder();
        cmd_builder.setCmdType(Client.Command.CommandType.Type_LuaCommand);

        Client.LuaCommand.Builder lua_builder = Client.LuaCommand.newBuilder();
        lua_builder.setAction("clear");
//        lua_builder.setIp((int) ipaddr_num);
//        lua_builder.setPort(String.valueOf(port));
        cmd_builder.setLuaCmd(lua_builder);

        Client.Command command = cmd_builder.build();

        // send request
        if (debug_mode)
            System.out.format("Sending request:\n%s\n", command.toString());
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        int ret = send_command(command.toByteArray(), baos);
        if (ret <= 0)
            throw new Exception("协议分析器连接超时或调用失败");

        lua_changed = true;
        commit_lua();
        return true;
    }
    
    public boolean ssl_setup(long ipaddr_num, int port, String path, String passwd) throws Exception {
        Client.Command.Builder cmd_builder = Client.Command.newBuilder();
        cmd_builder.setCmdType(Client.Command.CommandType.Type_SslCommand);

        Client.SslCommand.Builder ssl_builder = Client.SslCommand.newBuilder();
        if (path == null || path.isEmpty()) {
            ssl_builder.setAction("del");
            ssl_builder.setIp((int) ipaddr_num);
            ssl_builder.setPort(port);
        } else {
            //校验文件是否存在
            File cert_file = new File(path);
            if(!cert_file.exists() || !cert_file.isFile()) {
                throw new Exception(path + " not exists or is not FILE.");
            }

            StringBuilder cert_content = new StringBuilder();
            FileInputStream in = null;
            try {
                in = new FileInputStream(cert_file);
                int ch;
                while( (ch=in.read()) != -1) {
                    cert_content.append((char)ch);
                }
            } finally {
                try{in.close();}catch(Exception e){}
            }

            ssl_builder.setAction("add");
            ssl_builder.setIp((int) ipaddr_num);
            ssl_builder.setPort(port);
            //ssl_builder.setCert(path);
            ssl_builder.setCertContent(cert_content.toString());
            if (passwd != null && passwd.length() > 0)
                ssl_builder.setPasswd(passwd);
        }
        cmd_builder.setSslCmd(ssl_builder);

        Client.Command command = cmd_builder.build();
        if (debug_mode) {
            System.out.println(command.toString());
        }
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        int ret = send_command(command.toByteArray(), baos);
        if (ret <= 0)
            throw new Exception("协议分析器连接超时或调用失败");

        ssl_changed = true;
        commit_ssl();
        return true;
    }

    //样本包抓包
    public boolean pcap_dump(long ipaddr_num, int time_in_sec, String filename_tosave) throws Exception {
        if (filename_tosave == null || filename_tosave.isEmpty())
            throw new Exception("Please input filename to save first!");

        Client.Command.Builder cmd_builder = Client.Command.newBuilder();
        cmd_builder.setCmdType(Client.Command.CommandType.Type_ExportCommand);

        Client.ExportCommand.Builder export_builder = Client.ExportCommand.newBuilder();
        export_builder.setIp((int) ipaddr_num);
        export_builder.setTime(time_in_sec);
        export_builder.setDumpFilename(filename_tosave);
        cmd_builder.setExportCmd(export_builder);

        Client.Command command = cmd_builder.build();

        // send request
        if (debug_mode)
            System.out.format("Sending request:\n%s\n", command.toString());
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        int ret = send_command(command.toByteArray(), baos);
        if (ret <= 0)
            throw new Exception("协议分析器连接超时或调用失败");

        return true;
    }
    
    //初始化协议分析器所有的配置
    public boolean init() throws Exception {
        Client.Command.Builder cmd_builder = Client.Command.newBuilder();
        cmd_builder.setCmdType(Client.Command.CommandType.Type_ClearCommand);
        Client.Command command = cmd_builder.build();
        // send request
        if (debug_mode)
            System.out.format("Sending request:\n%s\n", command.toString());
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        int ret = send_command(command.toByteArray(), baos);
        if (ret <= 0)
            throw new Exception("协议分析器连接超时或调用失败");
        return true;
    }

}